from osc_base import OSCBaseObject

def make_css(color_list):
    if type(color_list) == str:
        return color_list
    return '#%s' % (''.join(['%02x' % (i) for i in color_list]))

class BaseWidget(OSCBaseObject):
    _widget_type = None
    _saved_attributes = ['name', 'type', 'min', 'max', 'oninit', 'ontouchstart']
    _attribute_defaults = {'min':0, 'max':1}
    def __init__(self, **kwargs):
        self.link_state = False
        self.name = kwargs.get('name')
        self.id = kwargs.get('id', self.name)
        kwargs.setdefault('osc_address', self.id)
        super(BaseWidget, self).__init__(**kwargs)
        self.osc_root_address = kwargs.get('osc_root_address', 'iOSCControl')
        self.osc_client_address = kwargs.get('osc_client_address')
        self.type = self.__class__._widget_type
        for key in ['oninit', 'onvaluechange']:
            if key in kwargs:
                setattr(self, key, kwargs[key])
        
        cls = self.__class__
        while cls != OSCBaseObject:
            defaults = getattr(cls, '_attribute_defaults', None)
            if defaults:
                for key, val in defaults.iteritems():
                    if not hasattr(self, key) and kwargs.get(key) != val:
                        setattr(self, key, kwargs.get(key))
            cls = cls.__bases__[0]
        
        self.add_osc_handler(callbacks={'set-value':self.on_osc_set_value})
        
        self.child_widgets = {}
        
        self.src_signal = None
        self.set_src_object(**kwargs)
            
    def set_link_state(self, state):
        if state != self.link_state:
            if state:
                self.add_osc_handler(callbacks={'set-value':self.on_osc_set_value})
                if self.src_signal:
                    self.src_object.connect(self.src_signal, self.on_src_object_update)
            else:
                self.remove_osc_handler(id=self.osc_address)
                if self.src_signal:
                    self.src_object.disconnect(callback=self.on_src_object_update)
        self.link_state = state
    
    def set_src_object(self, **kwargs):
        if self.link_state and self.src_signal:
            self.src_object.disconnect(callback=self.on_src_object_update)
        self.src_object = kwargs.get('src_object')
        self.src_attr = kwargs.get('src_attr')
        self.src_signal = kwargs.get('src_signal')
        if self.link_state and self.src_signal:
            id = self.src_object.connect(self.src_signal, self.on_src_object_update)
            print 'widget connect: ', self.name, id, self.src_object
        
    def add_widget(self, cls, **kwargs):
        for key in ['osc_root_address', 'osc_client_address', 'osc_parent_node']:
            kwargs.setdefault(key, getattr(self, key))
        widget = cls(**kwargs)
        self.child_widgets.update({widget.id:widget})
        
    def send_value_to_osc(self, value):
        self.osc_node.send_message(root_address=self.osc_client_address, address='set-value', value=value)
    
    def on_osc_set_value(self, **kwargs):
        pass
        
    def on_src_object_update(self, **kwargs):
        pass
        
    def build_interface_dict(self, **kwargs):
        root = kwargs.get('root_address')
        d = {}
        for key in self.saved_attributes:
            val = getattr(self, key, None)
            if hasattr(self, key) and type(val) != set:
                d.update({key:val})
        path = self.osc_node.get_full_path()
        if root:
            path[0] = root
        path[1] = self.osc_root_address
        s = '/' + '/'.join(path) + '/set-value'
        d.update({'address':s})
        if 'osc_address' in d:
            del d['osc_address']
        l = [d]
        for w in self.child_widgets.itervalues():
            l += w.build_interface_dict(**kwargs)
        return l
        
class GraphicsWidget(BaseWidget):
    _saved_attributes = ['bounds', 'label', 'colors', 'ontouchstart', 'ontouchmove', 'ontouchend']#, 'backgroundColor', 'foregroundColor', 'strokeColor']
    def __init__(self, **kwargs):
        super(GraphicsWidget, self).__init__(**kwargs)
        if 'bounds' in kwargs:
            self.bounds = kwargs.get('bounds')
        else:
            self.x = kwargs.get('x', 0)
            self.y = kwargs.get('y', 0)
            self.width = kwargs.get('width', .25)
            self.height = kwargs.get('height', .25)
        self.backgroundColor = kwargs.get('backgroundColor', [128]*4)
        self.foregroundColor = kwargs.get('foregroundColor', [255]*3)
        self.strokeColor = kwargs.get('strokeColor', [255]*3)
        for s in ['start', 'move', 'end']:
            key = 'ontouch' + s
            if key in kwargs:
                setattr(self, key, kwargs[key])
        if 'label' in kwargs:
            self.label = kwargs.get('label')
            
    @property
    def backgroundColor(self):
        return make_css(self._backgroundColor)
    @backgroundColor.setter
    def backgroundColor(self, value):
        self._backgroundColor = value
    @property
    def foregroundColor(self):
        return make_css(self._foregroundColor)
    @foregroundColor.setter
    def foregroundColor(self, value):
        self._foregroundColor = value
    @property
    def strokeColor(self):
        return make_css(self._strokeColor)
    @strokeColor.setter
    def strokeColor(self, value):
        self._strokeColor = value
    @property
    def bounds(self):
        return [getattr(self, key) for key in ['x', 'y', 'width', 'height']]
    @bounds.setter
    def bounds(self, value):
        for x, key in enumerate(['x', 'y', 'width', 'height']):
            setattr(self, key, value[x])
    @property
    def colors(self):
        return [getattr(self, key) for key in ['backgroundColor', 'foregroundColor', 'strokeColor']]
        
class Button(GraphicsWidget):
    _widget_type = 'Button'
    _saved_attributes = ['mode']
    _attribute_defaults = {'mode':'toggle'}
    def __init__(self, **kwargs):
        self._touch_state = False
        kwargs.setdefault('width', .1)
        kwargs.setdefault('height', .1)
        kwargs.setdefault('mode', 'momentary')
        super(Button, self).__init__(**kwargs)
        self.register_signal('state_changed')
        
    @property
    def touch_state(self):
        return self._touch_state
    @touch_state.setter
    def touch_state(self, value):
        if value != self.touch_state:
            if value:
                i = 1.
            else:
                i = 0.
            self.send_value_to_osc(i)
        
    def on_osc_set_value(self, **kwargs):
        state = kwargs.get('values')[0] > 0
        if state != self.touch_state:
            self._touch_state = state
            self.emit('state_changed', widget=self, state=state)
        
class Toggle(Button):
    _widget_type = 'Button'
    pass

class Slider(GraphicsWidget):
    _widget_type = 'Slider'
    _saved_attributes = ['isVertical', 'isXFader']
    _attribute_defaults = {'isVertical':False, 'isXFader':False}
    def __init__(self, **kwargs):
        self._value = 0.
        super(Slider, self).__init__(**kwargs)
        self.register_signal('value_update')
        self.color = [100]*3
        self.stroke = [255]*3
        
    @property
    def value(self):
        return self._value
    @value.setter
    def value(self, value):
        if value != self.value:
            self._value = value
            self.send_value_to_osc(value)
            self.emit('value_update', widget=self, value=value)
            
    def on_osc_set_value(self, **kwargs):
        value = kwargs.get('values')[0]
        if value != self.value:
            self._value = value
            if self.src_object:
                setattr(self.src_object, self.src_attr, value)
            self.emit('value_update', widget=self, value=value)
        
    def on_src_object_update(self, **kwargs):
        value = getattr(self.src_object, self.src_attr)
        if value != self.value:
            self.value = value
        
class VSlider(Slider):
    _widget_type = 'Slider'
    def __init__(self, **kwargs):
        self.isVertical = True
        super(VSlider, self).__init__(**kwargs)
        
class HSlider(Slider):
    _widget_type = 'Slider'

class Label(GraphicsWidget):
    _widget_type = 'Label'
    _saved_attributes = ['verticalCenter', 'align', 'value']
    def __init__(self, **kwargs):
        self._value = None
        self._backgroundColor = [0, 0, 0, 0]
        super(Label, self).__init__(**kwargs)
        self.verticalCenter = kwargs.get('verticalCenter', True)
        self.align = kwargs.get('align', 'center')
        self.value = kwargs.get('value')
        self.strokeColor = [0, 255, 0]
        
    @property
    def value(self):
        return self._value
    @value.setter
    def value(self, value):
        if value != self.value:
            self.send_value_to_osc(value)
            
    def on_osc_set_value(self, **kwargs):
        value = kwargs.get('values')[0]
        if value != self.value:
            self._value = value
            self.emit('value_update', widget=self, value=value)
            
class xyPad(GraphicsWidget):
    _widget_type = 'MultiTouchXY'
    _saved_attributes = ['isMomentary', 'numberOfTouches']
    def __init__(self, **kwargs):
        self._pos = [0, 0]
        super(xyPad, self).__init__(**kwargs)
        self.register_signal('value_update')
        self.isMomentary = kwargs.get('isMomentary', False)
        self.xyInverted = kwargs.get('xyInverted', (False, True))
        self.numberOfTouches = 1
        self.scale_factor = kwargs.get('scale_factor', 1)
        
    @property
    def pos(self):
        return self._pos
    @pos.setter
    def pos(self, value):
        if value != self.pos:
            xy = [x / self.max for x in value]
            self._invert_xy(xy)
            self.send_value_to_osc(xy)
            
    def _invert_xy(self, xy):
        for i in range(2):
            if self.xyInverted[i]:
                xy[i] = xy[i] * -1
                
    def on_osc_set_value(self, **kwargs):
        xy = kwargs.get('values')
        self._invert_xy(xy)
        if xy[0] >= 0 and xy[1] >= 0 and xy != self.pos:
            self._pos = xy
            #print self.name, xy
            if self.src_object:
                setattr(self.src_object, self.src_attr, xy)
            self.emit('value_update', widget=self, value=self.pos)
            
    def on_src_object_update(self, **kwargs):
        value = getattr(self.src_object, self.src_attr)
        if value != self.pos:
            #print 'xy obj update:', value
            self.pos = value

w_list = [Button, Toggle, VSlider, HSlider, Label, xyPad]
widget_classes = dict(zip([cls.__name__ for cls in w_list], w_list))
